/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * ArugulaProjectPanel.java
 *
 * Created on 22-mar-2010, 11.04.19
 */

package ch.epfl.dslab.conferrng.wizard;

import ch.epfl.dslab.conferrng.arugula.ErrorGenerator;
import ch.epfl.dslab.conferrng.arugula.IOperator.Factory;
import ch.epfl.dslab.conferrng.arugula.Operator;
import ch.epfl.dslab.conferrng.engine.FaultInjectionPlan;
import ch.epfl.dslab.conferrng.engine.PluginFactory;
import ch.epfl.dslab.conferrng.gui.arugula.ArugulaTreeCellEditor;
import ch.epfl.dslab.conferrng.gui.arugula.ConfErrEditor;
import java.awt.Panel;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.event.KeyEvent;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTree;
import javax.swing.ToolTipManager;
import javax.swing.TransferHandler;
import javax.swing.TransferHandler.TransferSupport;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;

/**
 *
 * @author lokeller
 */
public class ArugulaProjectPanel extends javax.swing.JPanel {

    /** Creates new form ArugulaProjectPanel */
    public ArugulaProjectPanel() {
        initComponents();
        setupTreeTransferHandler();
        setPlan(null);

        ToolTipManager.sharedInstance().registerComponent(jTree1);
        
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        jSplitPane1 = new javax.swing.JSplitPane();
        jScrollPane1 = new javax.swing.JScrollPane();
        jTree1 = new javax.swing.JTree();
        editorPanel = new javax.swing.JPanel();

        setLayout(new java.awt.BorderLayout());

        jSplitPane1.setDividerLocation(200);
        jSplitPane1.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);

        jTree1.setCellEditor(new ArugulaTreeCellEditor(jTree1));
        jTree1.setCellRenderer(new ErrorGeneratorCellRenderer());
        jTree1.setDragEnabled(true);
        jTree1.setDropMode(javax.swing.DropMode.ON_OR_INSERT);
        jTree1.setEditable(true);
        jTree1.addTreeSelectionListener(new javax.swing.event.TreeSelectionListener() {
            public void valueChanged(javax.swing.event.TreeSelectionEvent evt) {
                jTree1ValueChanged(evt);
            }
        });
        jTree1.addKeyListener(new java.awt.event.KeyAdapter() {
            public void keyReleased(java.awt.event.KeyEvent evt) {
                jTree1KeyReleased(evt);
            }
        });
        jScrollPane1.setViewportView(jTree1);

        jSplitPane1.setLeftComponent(jScrollPane1);

        editorPanel.setLayout(new java.awt.BorderLayout());
        jSplitPane1.setRightComponent(editorPanel);

        add(jSplitPane1, java.awt.BorderLayout.CENTER);
    }// </editor-fold>//GEN-END:initComponents

    private void jTree1KeyReleased(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_jTree1KeyReleased
            if ( evt.getKeyCode() == KeyEvent.VK_DELETE) {
                TreePath path = jTree1.getSelectionPath();
                Object node = path.getLastPathComponent();

                // root can't be deleted
                if ( node == jTree1.getModel().getRoot()) {
                    return;
                }

                if ( node instanceof ErrorGenerator ) {
                    plan.removeErrorGenerator((ErrorGenerator) node);
                } else {

                    Operator parent = ((Operator) node).getParent();
                    int pos = parent.getChildren().indexOf(node);
                    parent.removeChild((Operator) node);

                    TreePath newPath = path.getParentPath();

                    if ( pos < parent.getChildren().size() ) {
                        newPath = newPath.pathByAddingChild(parent.getChildren().get(pos));
                    } else if ( parent.getChildren().size() > 0 ) {
                        newPath = newPath.pathByAddingChild(parent.getChildren().get(parent.getChildren().size() - 1));
                    }
                    jTree1.makeVisible(newPath);
                    jTree1.scrollRectToVisible(jTree1.getPathBounds(newPath));
                }
            }
    }//GEN-LAST:event_jTree1KeyReleased

    private void jTree1ValueChanged(javax.swing.event.TreeSelectionEvent evt) {//GEN-FIRST:event_jTree1ValueChanged

         editorPanel.removeAll();

         if ( jTree1.getSelectionPath() != null &&
                 jTree1.getSelectionPath().getLastPathComponent() != jTree1.getModel().getRoot()) {

                      JPanel p = new ConfErrEditor((Operator) jTree1.getSelectionPath().getLastPathComponent());
                      editorPanel.add(p);

         }

         editorPanel.validate();
    }//GEN-LAST:event_jTree1ValueChanged

    
    private FaultInjectionPlan plan;

    public FaultInjectionPlan getPlan() {
        return plan;
    }

    public void setPlan(FaultInjectionPlan plan) {
        System.err.println("========================="+plan);
        this.plan = plan;
        if ( plan == null) {
            jTree1.setModel(new DefaultTreeModel(new DefaultMutableTreeNode("Project")));
        } else {
            jTree1.setModel(new ArugulaProjectTreeModel(plan));
        }
    }


    private void setupTreeTransferHandler() {
        jTree1.setTransferHandler(new TransferHandler() {



            @Override
            public boolean canImport(TransferHandler.TransferSupport info) {
                // for the demo, we'll only support drops (not clipboard paste)
                if (!info.isDrop()) {
                    return false;
                }

                if (plan == null) return false;

                
                info.setShowDropLocation(true);

                JTree.DropLocation dl = (JTree.DropLocation) info.getDropLocation();

                // fetch the path and child index from the drop location
                TreePath path = dl.getPath();

                if (path == null) return false;

                Object dropTarget = path.getLastPathComponent();

                // we only import Strings
                if (!info.isDataFlavorSupported(DataFlavor.stringFlavor) &&
                    !info.isDataFlavorSupported(flavor)) {
                    return false;
                }

                if (info.isDataFlavorSupported(DataFlavor.stringFlavor)) {
                    return canImportExternal(info, dropTarget);
                }

                if (info.isDataFlavorSupported(flavor)) {
                    return canImportInternal(info, dropTarget);
                }

                return true;
            }

            private boolean canImportInternal(TransferSupport info, Object dropTarget) {
                try {
                    Operator node = (Operator) info.getTransferable().getTransferData(flavor);
                    /* we are moving an operator */
                    if (node.getParent() != null) {
                        /* cannot move an operator in the faultplan */
                        if (dropTarget == jTree1.getModel().getRoot()) {
                            return false;
                        } else {
                            return true;
                        }
                        /* we are moving an error generator */
                        /* we are moving an error generator */
                    } else {
                        /* error generators can only be moved in root */
                        if (dropTarget != jTree1.getModel().getRoot()) {
                            return false;
                        } else {
                            return true;
                        }
                    }
                } catch (UnsupportedFlavorException ex) {
                    Logger.getLogger(ArugulaProjectPanel.class.getName()).log(Level.SEVERE, null, ex);
                } catch (IOException ex) {
                    Logger.getLogger(ArugulaProjectPanel.class.getName()).log(Level.SEVERE, null, ex);
                }
                return false;
            }

            private boolean canImportExternal(TransferSupport info, Object dropTarget) {
                try {
                    String pluginName = (String) info.getTransferable().getTransferData(DataFlavor.stringFlavor);
                    Factory f = PluginFactory.newInstance(pluginName, plan, null).getFactory();
                    Operator op = f.getACopy(null);
                    if (op instanceof ErrorGenerator) {
                        if (dropTarget == jTree1.getModel().getRoot()) {
                            return true;
                        } else {
                            return false;
                        }
                    } else {
                        if (dropTarget != jTree1.getModel().getRoot()) {
                            return true;
                        } else {
                            return false;
                        }
                    }
                } catch (UnsupportedFlavorException ex) {
                    Logger.getLogger(ArugulaProjectPanel.class.getName()).log(Level.SEVERE, null, ex);
                } catch (IOException ex) {
                    Logger.getLogger(ArugulaProjectPanel.class.getName()).log(Level.SEVERE, null, ex);
                } catch (RuntimeException ex) {
                    ex.printStackTrace();
                }
                return false;
            }

            @Override
            public boolean importData(TransferHandler.TransferSupport info) {

                // if we can't handle the import, say so
                if (!canImport(info)) {
                    return false;
                }

                if ( info.isDataFlavorSupported(flavor)) {
                    return internalDrop(info);
                } else {
                    return externalDrop(info);
                }

            }


                        @Override
            public int getSourceActions(JComponent c) {
                return COPY_OR_MOVE;
            }

            @Override
            protected Transferable createTransferable(JComponent c) {

                if (jTree1.getSelectionPath().getLastPathComponent() == jTree1.getModel().getRoot() ) {
                    return null;
                }

                return new Transferable() {

                    final Object node = jTree1.getSelectionPath().getLastPathComponent();

                    @Override
                    public DataFlavor[] getTransferDataFlavors() {

                        return new DataFlavor[] { flavor };
                    }

                    @Override
                    public boolean isDataFlavorSupported(DataFlavor sflavor) {
                        return flavor.equals(sflavor);
                    }

                    @Override
                    public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
                        return node;
                    }
                };

            }

            private boolean externalDrop(TransferHandler.TransferSupport info) {
                // fetch the drop location
                JTree.DropLocation dl = (JTree.DropLocation) info.getDropLocation();

                // fetch the path and child index from the drop location
                TreePath path = dl.getPath();
                int childIndex = dl.getChildIndex();

                // fetch the data and bail if this fails
                String pluginName;
                try {
                    pluginName = (String) info.getTransferable().getTransferData(DataFlavor.stringFlavor);
                } catch (UnsupportedFlavorException e) {
                    return false;
                } catch (IOException e) {
                    return false;
                }

                Object dropTarget = path.getLastPathComponent();

                try {

                    System.err.println("$$$$$$"+plan);
                    Factory f = PluginFactory.newInstance(pluginName, plan, null).getFactory();


                    Operator op = f.getACopy(null);
                    System.err.println("^^^^^^^^^^^^^^^"+op.getPlan());
                    if ( dropTarget == jTree1.getModel().getRoot()) {
                        if ( op instanceof ErrorGenerator) {
                            if ( childIndex == -1 ) {
                                plan.addErrorGenerator((ErrorGenerator) op);
                            } else {
                                plan.addErrorGenerator((ErrorGenerator) op, childIndex);
                            }
                        } else {
                            return false;
                        }
                    } else {
                        if ( childIndex == -1) {
                            ((Operator) dropTarget).addChild(op);
                        } else {
                            ((Operator) dropTarget).addChild(op, childIndex);
                        }
                    }

                    TreePath newPath = path.pathByAddingChild(op);
                    jTree1.makeVisible(newPath);
                    jTree1.scrollRectToVisible(jTree1.getPathBounds(newPath));

                } catch (RuntimeException ex) {
                    JOptionPane.showMessageDialog(ArugulaProjectPanel.this, ex.toString());
                    ex.printStackTrace();
                    return false;
                }

                return true;
            }

            private boolean internalDrop(TransferSupport info) {

                JTree.DropLocation dl = (JTree.DropLocation) info.getDropLocation();

                // fetch the path and child index from the drop location
                TreePath path = dl.getPath();
                int childIndex = dl.getChildIndex();
                Object dropTarget = path.getLastPathComponent();

                if ( info.getDropAction() == TransferHandler.MOVE) {
                    try {

                        Operator node = (Operator) info.getTransferable().getTransferData(flavor);

                        /* we are moving an operator */
                        if (node.getParent() != null) {

                            /* cannot move an operator in the faultplan */
                            if ( dropTarget == jTree1.getModel().getRoot()) {
                                return false;
                            }

                            /* moving inside the same parent */
                            int correction = 0;
                            if ( node.getParent() == dropTarget &&
                                    childIndex > node.getParent().getChildren().indexOf(node)) {
                                correction = -1;
                            }

                            node.getParent().removeChild(node);

                            if (childIndex == -1) {
                                ((Operator) dropTarget).addChild(node);
                            } else {
                                ((Operator) dropTarget).addChild(node, childIndex + correction);
                            }

                            TreePath newPath = path.pathByAddingChild(node);
                            jTree1.makeVisible(newPath);
                            jTree1.scrollRectToVisible(jTree1.getPathBounds(newPath));

                            return true;

                        /* we are moving an error generator */
                        } else {


                            /* error generators can only be moved in root */
                            if ( dropTarget != jTree1.getModel().getRoot()) {
                                return false;
                            }

                            /* fix offset if moving past current position */
                            int correction = 0;
                            if (  childIndex >= plan.getErrorGenerators().indexOf(node)) {
                                correction = -1;
                            }

                            plan.removeErrorGenerator((ErrorGenerator) node);

                            if (childIndex == -1) {
                                plan.addErrorGenerator((ErrorGenerator) node);
                            } else {
                                plan.addErrorGenerator((ErrorGenerator) node, childIndex + correction);
                            }

                            TreePath newPath = path.pathByAddingChild(node);
                            jTree1.makeVisible(newPath);
                            jTree1.scrollRectToVisible(jTree1.getPathBounds(newPath));

                            return true;
                        }

                    } catch (UnsupportedFlavorException ex) {
                        Logger.getLogger(ArugulaProjectPanel.class.getName()).log(Level.SEVERE, null, ex);
                    } catch (IOException ex) {
                        Logger.getLogger(ArugulaProjectPanel.class.getName()).log(Level.SEVERE, null, ex);
                    }

                    return false;

                } else {
                    return false;
                }

            }



        });
    }

    static DataFlavor flavor;

    static {

        try {
            flavor = new DataFlavor(DataFlavor.javaJVMLocalObjectMimeType +
            ";class=ch.epfl.dslab.conferrng.arugula.Operator");
        } catch (ClassNotFoundException ex) {}
    }


    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JPanel editorPanel;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JSplitPane jSplitPane1;
    private javax.swing.JTree jTree1;
    // End of variables declaration//GEN-END:variables

}
